// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCUDACXX_VARIANT
#define _LIBCUDACXX_VARIANT

/*
   variant synopsis

namespace std {

  // 20.7.2, class template variant
  template <class... Types>
  class variant {
  public:

    // 20.7.2.1, constructors
    constexpr variant() noexcept(see below);
    constexpr variant(const variant&);
    constexpr variant(variant&&) noexcept(see below);

    template <class T> constexpr variant(T&&) noexcept(see below);

    template <class T, class... Args>
    constexpr explicit variant(in_place_type_t<T>, Args&&...);

    template <class T, class U, class... Args>
    constexpr explicit variant(
        in_place_type_t<T>, initializer_list<U>, Args&&...);

    template <size_t I, class... Args>
    constexpr explicit variant(in_place_index_t<I>, Args&&...);

    template <size_t I, class U, class... Args>
    constexpr explicit variant(
        in_place_index_t<I>, initializer_list<U>, Args&&...);

    // 20.7.2.2, destructor
    ~variant();

    // 20.7.2.3, assignment
    constexpr variant& operator=(const variant&);
    constexpr variant& operator=(variant&&) noexcept(see below);

    template <class T> variant& operator=(T&&) noexcept(see below);

    // 20.7.2.4, modifiers
    template <class T, class... Args>
    T& emplace(Args&&...);

    template <class T, class U, class... Args>
    T& emplace(initializer_list<U>, Args&&...);

    template <size_t I, class... Args>
    variant_alternative_t<I, variant>& emplace(Args&&...);

    template <size_t I, class U, class...  Args>
    variant_alternative_t<I, variant>& emplace(initializer_list<U>, Args&&...);

    // 20.7.2.5, value status
    constexpr bool valueless_by_exception() const noexcept;
    constexpr size_t index() const noexcept;

    // 20.7.2.6, swap
    void swap(variant&) noexcept(see below);
  };

  // 20.7.3, variant helper classes
  template <class T> struct variant_size; // undefined

  template <class T>
  inline constexpr size_t variant_size_v = variant_size<T>::value;

  template <class T> struct variant_size<const T>;
  template <class T> struct variant_size<volatile T>;
  template <class T> struct variant_size<const volatile T>;

  template <class... Types>
  struct variant_size<variant<Types...>>;

  template <size_t I, class T> struct variant_alternative; // undefined

  template <size_t I, class T>
  using variant_alternative_t = typename variant_alternative<I, T>::type;

  template <size_t I, class T> struct variant_alternative<I, const T>;
  template <size_t I, class T> struct variant_alternative<I, volatile T>;
  template <size_t I, class T> struct variant_alternative<I, const volatile T>;

  template <size_t I, class... Types>
  struct variant_alternative<I, variant<Types...>>;

  inline constexpr size_t variant_npos = -1;

  // 20.7.4, value access
  template <class T, class... Types>
  constexpr bool holds_alternative(const variant<Types...>&) noexcept;

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>>&
  get(variant<Types...>&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>>&&
  get(variant<Types...>&&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>> const&
  get(const variant<Types...>&);

  template <size_t I, class... Types>
  constexpr variant_alternative_t<I, variant<Types...>> const&&
  get(const variant<Types...>&&);

  template <class T, class...  Types>
  constexpr T& get(variant<Types...>&);

  template <class T, class... Types>
  constexpr T&& get(variant<Types...>&&);

  template <class T, class... Types>
  constexpr const T& get(const variant<Types...>&);

  template <class T, class... Types>
  constexpr const T&& get(const variant<Types...>&&);

  template <size_t I, class... Types>
  constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
  get_if(variant<Types...>*) noexcept;

  template <size_t I, class... Types>
  constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
  get_if(const variant<Types...>*) noexcept;

  template <class T, class... Types>
  constexpr add_pointer_t<T>
  get_if(variant<Types...>*) noexcept;

  template <class T, class... Types>
  constexpr add_pointer_t<const T>
  get_if(const variant<Types...>*) noexcept;

  // 20.7.5, relational operators
  template <class... Types>
  constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);

  template <class... Types>
  constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);

  template <class... Types> requires (three_way_comparable<Types> && ...)
  constexpr common_comparison_category_t<compare_three_way_result_t<Types>...>
    operator<=>(const variant<Types...>&, const variant<Types...>&); // since
C++20

  // 20.7.6, visitation
  template <class Visitor, class... Variants>
  constexpr see below visit(Visitor&&, Variants&&...);

  template <class R, class Visitor, class... Variants>
  constexpr R visit(Visitor&&, Variants&&...); // since C++20

  // 20.7.7, class monostate
  struct monostate;

  // 20.7.8, monostate relational operators
  constexpr bool operator==(monostate, monostate) noexcept;
  constexpr bool operator!=(monostate, monostate) noexcept;             // until C++20
  constexpr bool operator<(monostate, monostate) noexcept;              // until C++20
  constexpr bool operator>(monostate, monostate) noexcept;              // until C++20
  constexpr bool operator<=(monostate, monostate) noexcept;             // until C++20
  constexpr bool operator>=(monostate, monostate) noexcept;             // until C++20
  constexpr strong_ordering operator<=>(monostate, monostate) noexcept; // since C++20

  // 20.7.9, specialized algorithms
  template <class... Types>
  void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);

  // 20.7.10, class bad_variant_access
  class bad_variant_access;

  // 20.7.11, hash support
  template <class T> struct hash;
  template <class... Types> struct hash<variant<Types...>>;
  template <> struct hash<monostate>;

} // namespace std

*/

#include <cuda/std/detail/__config>

#if defined(_CCCL_IMPLICIT_SYSTEM_HEADER_GCC)
#  pragma GCC system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_CLANG)
#  pragma clang system_header
#elif defined(_CCCL_IMPLICIT_SYSTEM_HEADER_MSVC)
#  pragma system_header
#endif // no system header

#if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
#  include <cuda/std/__compare/common_comparison_category.h>
#  include <cuda/std/__compare/compare_three_way_result.h>
#  include <cuda/std/__compare/three_way_comparable.h>
#endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
#include <cuda/std/__exception/terminate.h>
#include <cuda/std/__functional/hash.h>
#include <cuda/std/__functional/invoke.h>
#include <cuda/std/__functional/operations.h>
#include <cuda/std/__functional/unary_function.h>
#include <cuda/std/__memory/addressof.h>
#include <cuda/std/__memory/construct_at.h>
#include <cuda/std/__tuple_dir/tuple_indices.h>
#include <cuda/std/__type_traits/add_const.h>
#include <cuda/std/__type_traits/add_cv.h>
#include <cuda/std/__type_traits/add_pointer.h>
#include <cuda/std/__type_traits/add_volatile.h>
#include <cuda/std/__type_traits/dependent_type.h>
#include <cuda/std/__type_traits/integral_constant.h>
#include <cuda/std/__type_traits/is_array.h>
#include <cuda/std/__type_traits/is_destructible.h>
#include <cuda/std/__type_traits/is_nothrow_move_constructible.h>
#include <cuda/std/__type_traits/is_trivially_copy_assignable.h>
#include <cuda/std/__type_traits/is_trivially_copy_constructible.h>
#include <cuda/std/__type_traits/is_trivially_destructible.h>
#include <cuda/std/__type_traits/is_trivially_move_assignable.h>
#include <cuda/std/__type_traits/is_trivially_move_constructible.h>
#include <cuda/std/__type_traits/is_void.h>
#include <cuda/std/__type_traits/remove_const.h>
#include <cuda/std/__type_traits/remove_cvref.h>
#include <cuda/std/__type_traits/type_identity.h>
#include <cuda/std/__type_traits/type_list.h>
#include <cuda/std/__type_traits/void_t.h>
#include <cuda/std/__utility/declval.h>
#include <cuda/std/__utility/forward.h>
#include <cuda/std/__utility/in_place.h>
#include <cuda/std/__utility/integer_sequence.h>
#include <cuda/std/__utility/monostate.h>
#include <cuda/std/__utility/move.h>
#include <cuda/std/__utility/swap.h>
#include <cuda/std/__utility/unreachable.h>
#include <cuda/std/cstddef>
#include <cuda/std/initializer_list>
#include <cuda/std/tuple>
#include <cuda/std/version>

// [variant.syn]
#if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()
#  include <cuda/std/detail/libcxx/include/compare>
#endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()

#if _CCCL_HAS_EXCEPTIONS()
#  ifdef __cpp_lib_variant
#    include <variant>
#  else // ^^^ __cpp_lib_variant ^^^ / vvv !__cpp_lib_variant vvv
#    include <exception>
#  endif // !__cpp_lib_variant

#  include <cuda/std/__cccl/prologue.h>

_CCCL_BEGIN_NAMESPACE_CUDA_STD_NOVERSION

#  ifdef __cpp_lib_variant

using ::std::bad_variant_access;

#  else // ^^^ __cpp_lib_variant ^^^ / vvv !__cpp_lib_variant vvv
class _CCCL_TYPE_VISIBILITY_DEFAULT bad_variant_access : public ::std::exception
{
public:
  const char* what() const noexcept override
  {
    return "bad access to cuda::std::variant";
  }
};
#  endif // !__cpp_lib_variant

_CCCL_END_NAMESPACE_CUDA_STD_NOVERSION

#  include <cuda/std/__cccl/epilogue.h>

#endif // _CCCL_HAS_EXCEPTIONS()

#include <cuda/std/__cccl/prologue.h>

_CCCL_BEGIN_NAMESPACE_CUDA_STD

[[noreturn]] _CCCL_API inline void __throw_bad_variant_access()
{
#if _CCCL_HAS_EXCEPTIONS()
  NV_IF_ELSE_TARGET(
    NV_IS_HOST, (throw _CUDA_VSTD_NOVERSION::bad_variant_access();), (_CUDA_VSTD_NOVERSION::terminate();))
#else // ^^^ !_CCCL_HAS_EXCEPTIONS() ^^^ / vvv _CCCL_HAS_EXCEPTIONS() vvv
  _CUDA_VSTD_NOVERSION::terminate();
#endif // _CCCL_HAS_EXCEPTIONS()
}

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT variant;

template <class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_size;

template <class _Tp>
inline constexpr size_t variant_size_v = variant_size<_Tp>::value;

template <class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_size<const _Tp> : variant_size<_Tp>
{};

template <class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_size<volatile _Tp> : variant_size<_Tp>
{};

template <class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_size<const volatile _Tp> : variant_size<_Tp>
{};

template <class... _Types>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_size<variant<_Types...>> : integral_constant<size_t, sizeof...(_Types)>
{};

template <size_t _Ip, class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_alternative;

template <size_t _Ip, class _Tp>
using variant_alternative_t = typename variant_alternative<_Ip, _Tp>::type;

template <size_t _Ip, class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_alternative<_Ip, const _Tp> : add_const<variant_alternative_t<_Ip, _Tp>>
{};

template <size_t _Ip, class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT
variant_alternative<_Ip, volatile _Tp> : add_volatile<variant_alternative_t<_Ip, _Tp>>
{};

template <size_t _Ip, class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT
variant_alternative<_Ip, const volatile _Tp> : add_cv<variant_alternative_t<_Ip, _Tp>>
{};

template <size_t _Ip, class... _Types>
struct _CCCL_TYPE_VISIBILITY_DEFAULT variant_alternative<_Ip, variant<_Types...>>
{
  static_assert(_Ip < sizeof...(_Types), "Index out of bounds in _CUDA_VSTD::variant_alternative<>");
  using type = __type_index_c<_Ip, _Types...>;
};

inline constexpr size_t variant_npos = static_cast<size_t>(-1);

_CCCL_API constexpr int __choose_index_type(unsigned int __num_elem)
{
  constexpr unsigned char __small   = static_cast<unsigned char>(-1);
  constexpr unsigned short __medium = static_cast<unsigned short>(-1);
  if (__num_elem < static_cast<unsigned int>(__small))
  {
    return 0;
  }
  if (__num_elem < static_cast<unsigned int>(__medium))
  {
    return 1;
  }
  return 2;
}

template <size_t _NumAlts>
using __variant_index_t =
  conditional_t<__choose_index_type(_NumAlts) == 0,
                unsigned char,
                conditional_t<__choose_index_type(_NumAlts) == 1, unsigned short, unsigned int>>;

template <class _IndexType>
constexpr _IndexType __variant_npos = static_cast<_IndexType>(-1);

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT variant;

template <class... _Types>
_CCCL_API constexpr variant<_Types...>& __as_variant(variant<_Types...>& __vs) noexcept
{
  return __vs;
}

template <class... _Types>
_CCCL_API constexpr const variant<_Types...>& __as_variant(const variant<_Types...>& __vs) noexcept
{
  return __vs;
}

template <class... _Types>
_CCCL_API constexpr variant<_Types...>&& __as_variant(variant<_Types...>&& __vs) noexcept
{
  return _CUDA_VSTD::move(__vs);
}

template <class... _Types>
_CCCL_API constexpr const variant<_Types...>&& __as_variant(const variant<_Types...>&& __vs) noexcept
{
  return _CUDA_VSTD::move(__vs);
}

namespace __find_detail
{

template <class _Tp, class... _Types>
_CCCL_API constexpr size_t __find_index()
{
  constexpr bool __matches[] = {is_same_v<_Tp, _Types>...};
  size_t __result            = __not_found;
  for (size_t __i = 0; __i < sizeof...(_Types); ++__i)
  {
    if (__matches[__i])
    {
      if (__result != __not_found)
      {
        return __ambiguous;
      }
      __result = __i;
    }
  }
  return __result;
}

template <size_t _Index>
struct __find_unambiguous_index_sfinae_impl : integral_constant<size_t, _Index>
{};

template <>
struct __find_unambiguous_index_sfinae_impl<__not_found>
{};

template <>
struct __find_unambiguous_index_sfinae_impl<__ambiguous>
{};

template <class _Tp, class... _Types>
struct __find_unambiguous_index_sfinae : __find_unambiguous_index_sfinae_impl<__find_index<_Tp, _Types...>()>
{};

} // namespace __find_detail

namespace __variant_detail
{

struct __valueless_t
{};

enum class _Trait
{
  _TriviallyAvailable,
  _Available,
  _Unavailable
};

template <typename _Tp, template <typename> class _IsTriviallyAvailable, template <typename> class _IsAvailable>
constexpr _Trait __trait =
  _IsTriviallyAvailable<_Tp>::value ? _Trait::_TriviallyAvailable
  : _IsAvailable<_Tp>::value
    ? _Trait::_Available
    : _Trait::_Unavailable;

_CCCL_API constexpr _Trait __common_trait(initializer_list<_Trait> __traits)
{
  _Trait __result = _Trait::_TriviallyAvailable;
  for (_Trait __t : __traits)
  {
    if (static_cast<int>(__t) > static_cast<int>(__result))
    {
      __result = __t;
    }
  }
  return __result;
}

template <typename... _Types>
struct __traits
{
  static constexpr _Trait __copy_constructible_trait =
    __variant_detail::__common_trait({__trait<_Types, is_trivially_copy_constructible, is_copy_constructible>...});

  static constexpr _Trait __move_constructible_trait =
    __variant_detail::__common_trait({__trait<_Types, is_trivially_move_constructible, is_move_constructible>...});

  static constexpr _Trait __copy_assignable_trait = __variant_detail::__common_trait(
    {__copy_constructible_trait, __trait<_Types, is_trivially_copy_assignable, is_copy_assignable>...});

  static constexpr _Trait __move_assignable_trait = __variant_detail::__common_trait(
    {__move_constructible_trait, __trait<_Types, is_trivially_move_assignable, is_move_assignable>...});

  static constexpr _Trait __destructible_trait =
    __variant_detail::__common_trait({__trait<_Types, is_trivially_destructible, is_destructible>...});
};

namespace __access
{

struct __union
{
  template <class _Vp>
  _CCCL_API static constexpr auto&& __get_alt(_Vp&& __v, in_place_index_t<0>)
  {
    return _CUDA_VSTD::forward<_Vp>(__v).__head;
  }

  template <class _Vp, size_t _Ip>
  _CCCL_API static constexpr auto&& __get_alt(_Vp&& __v, in_place_index_t<_Ip>)
  {
    return __get_alt(_CUDA_VSTD::forward<_Vp>(__v).__tail, in_place_index<_Ip - 1>);
  }
};

struct __base
{
  template <size_t _Ip, class _Vp>
  _CCCL_API static constexpr auto&& __get_alt(_Vp&& __v)
  {
    return __union::__get_alt(_CUDA_VSTD::forward<_Vp>(__v).__data, in_place_index<_Ip>);
  }
};

struct __variant
{
  template <size_t _Ip, class _Vp>
  _CCCL_API static constexpr auto&& __get_alt(_Vp&& __v)
  {
    return __base::__get_alt<_Ip>(_CUDA_VSTD::forward<_Vp>(__v).__impl_);
  }
};

} // namespace __access

namespace __visitation
{
struct __variant
{
  // We need to guard against the final invocation where we have processed all variants
  template <size_t _Remaining, size_t _CurrentVariant, class... _Variants, enable_if_t<_Remaining == 0, int> = 0>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr size_t __get_runtime_index(const _Variants&...) noexcept
  {
    return 0;
  }

  template <size_t _Remaining,
            size_t _CurrentVariant,
            class _Variant,
            class... _OtherVariants,
            enable_if_t<(_Remaining != 0) && (_CurrentVariant == 0), int> = 0>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr size_t
  __get_runtime_index(const _Variant& __v, const _OtherVariants&... __vs) noexcept
  {
    return __v.__impl_.index();
  }

  template <size_t _Remaining,
            size_t _CurrentVariant,
            class _Variant,
            class... _OtherVariants,
            enable_if_t<(_Remaining != 0) && (_CurrentVariant != 0), int> = 0>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr size_t
  __get_runtime_index(const _Variant& __v, const _OtherVariants&... __vs) noexcept
  {
    return __get_runtime_index<_Remaining, _CurrentVariant - 1>(__vs...);
  }

  // Terminal function call with all indexes determined
  template <class _Visitor, class... _Vs, size_t... _ProcessedIndices>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr decltype(auto) __visit_impl(
    integer_sequence<size_t, _ProcessedIndices...>,
    integer_sequence<size_t>,
    const size_t,
    _Visitor&& __visitor,
    _Vs&&... __vs)
  {
    return _CUDA_VSTD::__invoke(
      _CUDA_VSTD::forward<_Visitor>(__visitor),
      __access::__base::__get_alt<_ProcessedIndices>(_CUDA_VSTD::forward<_Vs>(__vs).__impl_)...);
  }

  template <size_t _CurrentIndex,
            class _Visitor,
            class... _Vs,
            size_t... _ProcessedIndices,
            size_t... _UnprocessedIndices,
            enable_if_t<_CurrentIndex != 0, int> = 0>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr decltype(auto) __visit_impl(
    integer_sequence<size_t, _ProcessedIndices...>,
    integer_sequence<size_t, _CurrentIndex, _UnprocessedIndices...>,
    const size_t __current_index,
    _Visitor&& __visitor,
    _Vs&&... __vs)
  {
    // We found the right index, move to the next variant
    if (__current_index == _CurrentIndex)
    {
      const size_t __next_index =
        __get_runtime_index<sizeof...(_UnprocessedIndices), sizeof...(_ProcessedIndices) + 1>(__vs...);
      return __visit_impl(
        integer_sequence<size_t, _ProcessedIndices..., _CurrentIndex>{},
        integer_sequence<size_t, _UnprocessedIndices...>{},
        __next_index,
        _CUDA_VSTD::forward<_Visitor>(__visitor),
        _CUDA_VSTD::forward<_Vs>(__vs)...);
    }

    return __visit_impl(
      integer_sequence<size_t, _ProcessedIndices...>{},
      integer_sequence<size_t, _CurrentIndex - 1, _UnprocessedIndices...>{},
      __current_index,
      _CUDA_VSTD::forward<_Visitor>(__visitor),
      _CUDA_VSTD::forward<_Vs>(__vs)...);
  }

  _CCCL_BEGIN_NV_DIAG_SUPPRESS(940) // Suppress no return at end of function
  // This overload is needed to tell the compiler that the recursion is indeed limited
  template <class _Visitor, class... _Vs, size_t... _ProcessedIndices, size_t... _UnprocessedIndices>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr decltype(auto) __visit_impl(
    integer_sequence<size_t, _ProcessedIndices...>,
    integer_sequence<size_t, 0, _UnprocessedIndices...>,
    const size_t __current_index,
    _Visitor&& __visitor,
    _Vs&&... __vs)
  {
    // We found the right index, move to the next variant
    if (__current_index == 0)
    {
      const size_t __next_index =
        __get_runtime_index<sizeof...(_UnprocessedIndices), sizeof...(_ProcessedIndices) + 1>(__vs...);
      return __visit_impl(
        integer_sequence<size_t, _ProcessedIndices..., 0>{},
        integer_sequence<size_t, _UnprocessedIndices...>{},
        __next_index,
        _CUDA_VSTD::forward<_Visitor>(__visitor),
        _CUDA_VSTD::forward<_Vs>(__vs)...);
    }
    _CCCL_UNREACHABLE();
  }
  _CCCL_END_NV_DIAG_SUPPRESS() // End suppression of no return at end of function

  template <class _Visitor, class... _Vs>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr decltype(auto)
  __visit_value(_Visitor&& __visitor, _Vs&&... __vs)
  {
    // NOTE: We use a recursive implementation strategy here. That means we can omit the manual return type checks from
    // the common function pointer implementation, as the compiler will abort if the return types do not match.
    const size_t __first_index = __get_runtime_index<sizeof...(_Vs), 0>(__vs...);
    return __visit_impl(
      integer_sequence<size_t>{},
      integer_sequence<size_t, (remove_cvref_t<_Vs>::__size() - 1)...>{},
      __first_index,
      __make_value_visitor(_CUDA_VSTD::forward<_Visitor>(__visitor)),
      _CUDA_VSTD::forward<_Vs>(__vs)...);
  }

  template <class _Rp, class _Visitor, class... _Vs>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr _Rp __visit_value(_Visitor&& __visitor, _Vs&&... __vs)
  {
    const size_t __first_index = __get_runtime_index<sizeof...(_Vs), 0>(__vs...);
    return __visit_impl(
      integer_sequence<size_t>{},
      integer_sequence<size_t, (remove_cvref_t<_Vs>::__size() - 1)...>{},
      __first_index,
      __make_value_visitor<_Rp>(_CUDA_VSTD::forward<_Visitor>(__visitor)),
      _CUDA_VSTD::forward<_Vs>(__vs)...);
  }

private:
  template <class _Visitor, class... _Values>
  _CCCL_API static constexpr void __std_visit_exhaustive_visitor_check()
  {
    static_assert(is_invocable_v<_Visitor, _Values...>, "`std::visit` requires the visitor to be exhaustive.");
  }

  template <class _Visitor>
  struct __value_visitor
  {
    template <class... _Alts>
    _CCCL_API constexpr decltype(auto) operator()(_Alts&&... __alts) const
    {
      __std_visit_exhaustive_visitor_check<_Visitor, decltype((_CUDA_VSTD::forward<_Alts>(__alts).__value))...>();
      return _CUDA_VSTD::__invoke(
        _CUDA_VSTD::forward<_Visitor>(__visitor), _CUDA_VSTD::forward<_Alts>(__alts).__value...);
    }
    _Visitor&& __visitor;
  };

  template <class _Rp, class _Visitor>
  struct __value_visitor_return_type
  {
    template <class... _Alts>
    _CCCL_API constexpr _Rp operator()(_Alts&&... __alts) const
    {
      __std_visit_exhaustive_visitor_check<_Visitor, decltype((_CUDA_VSTD::forward<_Alts>(__alts).__value))...>();
      return _CUDA_VSTD::__invoke(
        _CUDA_VSTD::forward<_Visitor>(__visitor), _CUDA_VSTD::forward<_Alts>(__alts).__value...);
    }
    _Visitor&& __visitor;
  };

  template <class _Visitor>
  struct __value_visitor_return_type<void, _Visitor>
  {
    template <class... _Alts>
    _CCCL_API constexpr void operator()(_Alts&&... __alts) const
    {
      __std_visit_exhaustive_visitor_check<_Visitor, decltype((_CUDA_VSTD::forward<_Alts>(__alts).__value))...>();
      _CUDA_VSTD::__invoke(_CUDA_VSTD::forward<_Visitor>(__visitor), _CUDA_VSTD::forward<_Alts>(__alts).__value...);
    }
    _Visitor&& __visitor;
  };

  template <class _Visitor>
  _CCCL_API static constexpr auto __make_value_visitor(_Visitor&& __visitor)
  {
    return __value_visitor<_Visitor>{_CUDA_VSTD::forward<_Visitor>(__visitor)};
  }

  template <class _Rp, class _Visitor>
  _CCCL_API static constexpr auto __make_value_visitor(_Visitor&& __visitor)
  {
    return __value_visitor_return_type<_Rp, _Visitor>{_CUDA_VSTD::forward<_Visitor>(__visitor)};
  }
};

} // namespace __visitation

template <size_t _Index, class _Tp>
struct _CCCL_TYPE_VISIBILITY_DEFAULT __alt
{
  using __value_type = _Tp;

  _CCCL_EXEC_CHECK_DISABLE
  template <class... _Args>
  _CCCL_API explicit constexpr __alt(in_place_t, _Args&&... __args)
      : __value(_CUDA_VSTD::forward<_Args>(__args)...)
  {}
  _CCCL_EXEC_CHECK_DISABLE
  constexpr __alt(const __alt&) = default;
  _CCCL_EXEC_CHECK_DISABLE
  constexpr __alt(__alt&&) = default;
  _CCCL_EXEC_CHECK_DISABLE
  constexpr __alt& operator=(const __alt&) = default;
  _CCCL_EXEC_CHECK_DISABLE
  constexpr __alt& operator=(__alt&&) = default;

  _CCCL_EXEC_CHECK_DISABLE
  ~__alt() = default;

  __value_type __value;
};

template <_Trait _DestructibleTrait, size_t _Index, class... _Types>
union _CCCL_TYPE_VISIBILITY_DEFAULT __union;

template <_Trait _DestructibleTrait, size_t _Index>
union _CCCL_TYPE_VISIBILITY_DEFAULT __union<_DestructibleTrait, _Index>
{};

#define _LIBCUDACXX_VARIANT_UNION_BODY(destructible_trait)                       \
                                                                                 \
private:                                                                         \
  char __dummy;                                                                  \
  __alt<_Index, _Tp> __head;                                                     \
  __union<destructible_trait, _Index + 1, _Types...> __tail;                     \
                                                                                 \
  friend struct __access::__union;                                               \
                                                                                 \
public:                                                                          \
  _CCCL_API explicit constexpr __union(__valueless_t) noexcept                   \
      : __dummy{}                                                                \
  {}                                                                             \
                                                                                 \
  template <class... _Args>                                                      \
  _CCCL_API explicit constexpr __union(in_place_index_t<0>, _Args&&... __args)   \
      : __head(in_place, _CUDA_VSTD::forward<_Args>(__args)...)                  \
  {}                                                                             \
                                                                                 \
  template <size_t _Ip, class... _Args>                                          \
  _CCCL_API explicit constexpr __union(in_place_index_t<_Ip>, _Args&&... __args) \
      : __tail(in_place_index<_Ip - 1>, _CUDA_VSTD::forward<_Args>(__args)...)   \
  {}                                                                             \
                                                                                 \
  _CCCL_HIDE_FROM_ABI __union(const __union&)            = default;              \
  _CCCL_HIDE_FROM_ABI __union(__union&&)                 = default;              \
  _CCCL_HIDE_FROM_ABI __union& operator=(const __union&) = default;              \
  _CCCL_HIDE_FROM_ABI __union& operator=(__union&&)      = default;

template <size_t _Index, class _Tp, class... _Types>
union _CCCL_TYPE_VISIBILITY_DEFAULT __union<_Trait::_TriviallyAvailable, _Index, _Tp, _Types...>
{
  _LIBCUDACXX_VARIANT_UNION_BODY(_Trait::_TriviallyAvailable)
  _CCCL_HIDE_FROM_ABI ~__union() = default;
};

template <size_t _Index, class _Tp, class... _Types>
union _CCCL_TYPE_VISIBILITY_DEFAULT __union<_Trait::_Available, _Index, _Tp, _Types...>
{
  _LIBCUDACXX_VARIANT_UNION_BODY(_Trait::_Available)
  _CCCL_API inline ~__union() {}
};

template <size_t _Index, class _Tp, class... _Types>
union _CCCL_TYPE_VISIBILITY_DEFAULT __union<_Trait::_Unavailable, _Index, _Tp, _Types...>
{
  _LIBCUDACXX_VARIANT_UNION_BODY(_Trait::_Unavailable)
  _CCCL_API inline ~__union() = delete;
};

#undef _LIBCUDACXX_VARIANT_UNION_BODY

template <_Trait _DestructibleTrait, class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT __base
{
public:
  using __index_t = __variant_index_t<sizeof...(_Types)>;

  _CCCL_API explicit constexpr __base(__valueless_t __tag) noexcept
      : __data(__tag)
      , __index(__variant_npos<__index_t>)
  {}

  template <size_t _Ip, class... _Args>
  _CCCL_API explicit constexpr __base(in_place_index_t<_Ip>, _Args&&... __args)
      : __data(in_place_index<_Ip>, _CUDA_VSTD::forward<_Args>(__args)...)
      , __index(_Ip)
  {}

  _CCCL_API constexpr bool valueless_by_exception() const noexcept
  {
    return index() == variant_npos;
  }

  _CCCL_API constexpr size_t index() const noexcept
  {
    return __index == __variant_npos<__index_t> ? variant_npos : __index;
  }

protected:
  _CCCL_API constexpr auto&& __as_base() &
  {
    return *this;
  }

  _CCCL_API constexpr auto&& __as_base() &&
  {
    return _CUDA_VSTD::move(*this);
  }

  _CCCL_API constexpr auto&& __as_base() const&
  {
    return *this;
  }

  _CCCL_API constexpr auto&& __as_base() const&&
  {
    return _CUDA_VSTD::move(*this);
  }

  _CCCL_API static constexpr size_t __size()
  {
    return sizeof...(_Types);
  }

  __union<_DestructibleTrait, 0, _Types...> __data;
  __index_t __index;

  friend struct __access::__base;
};

template <class _Traits, _Trait = _Traits::__destructible_trait>
class _CCCL_TYPE_VISIBILITY_DEFAULT __dtor;

#define _LIBCUDACXX_VARIANT_DESTRUCTOR_BODY(destructible_trait)   \
  using __base_type = __base<destructible_trait, _Types...>;      \
  using __index_t   = typename __base_type::__index_t;            \
                                                                  \
public:                                                           \
  using __base_type::__base_type;                                 \
  using __base_type::operator=;                                   \
                                                                  \
  _CCCL_HIDE_FROM_ABI __dtor(const __dtor&)            = default; \
  _CCCL_HIDE_FROM_ABI __dtor(__dtor&&)                 = default; \
  _CCCL_HIDE_FROM_ABI __dtor& operator=(const __dtor&) = default; \
  _CCCL_HIDE_FROM_ABI __dtor& operator=(__dtor&&)      = default;

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT
__dtor<__traits<_Types...>, _Trait::_TriviallyAvailable> : public __base<_Trait::_TriviallyAvailable, _Types...>
{
  _LIBCUDACXX_VARIANT_DESTRUCTOR_BODY(_Trait::_TriviallyAvailable)
  _CCCL_HIDE_FROM_ABI ~__dtor() = default;

protected:
  _CCCL_API inline void __destroy() noexcept
  {
    this->__index = __variant_npos<__index_t>;
  }
};

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT
__dtor<__traits<_Types...>, _Trait::_Available> : public __base<_Trait::_Available, _Types...>
{
  struct __visitor
  {
    _CCCL_EXEC_CHECK_DISABLE
    template <class _Alt>
    _CCCL_API inline void operator()(_Alt& __alt) const noexcept
    {
      using __alt_type = remove_cvref_t<decltype(__alt)>;
      __alt.~__alt_type();
    }
  };

  _LIBCUDACXX_VARIANT_DESTRUCTOR_BODY(_Trait::_Available)
  _CCCL_API inline ~__dtor() noexcept
  {
    __destroy();
  }

protected:
  _CCCL_API inline void __destroy() noexcept
  {
    if (!this->valueless_by_exception())
    {
      constexpr size_t __np = remove_cvref_t<__dtor>::__size();
      __destroy(integral_constant<size_t, __np - 1>{}, this->__index);
    }
    this->__index = __variant_npos<__index_t>;
  }

private:
  template <size_t _CurrentIndex>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN void
  __destroy(integral_constant<size_t, _CurrentIndex>, const size_t __index) noexcept
  {
    if (__index == _CurrentIndex)
    {
      using __alt_type = remove_cvref_t<decltype(__access::__base::__get_alt<_CurrentIndex>(this->__as_base()))>;
      __access::__base::__get_alt<_CurrentIndex>(this->__as_base()).~__alt_type();
      return;
    }
    __destroy(integral_constant<size_t, _CurrentIndex - 1>{}, __index);
  }
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN void __destroy(integral_constant<size_t, 0>, const size_t __index) noexcept
  {
    if (__index == 0)
    {
      using __alt_type = remove_cvref_t<decltype(__access::__base::__get_alt<0>(this->__as_base()))>;
      __access::__base::__get_alt<0>(this->__as_base()).~__alt_type();
      return;
    }
    // We already checked that every variant has a value, so we should never reach this line
    _CCCL_UNREACHABLE();
  }
};

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT
__dtor<__traits<_Types...>, _Trait::_Unavailable> : public __base<_Trait::_Unavailable, _Types...>
{
  _LIBCUDACXX_VARIANT_DESTRUCTOR_BODY(_Trait::_Unavailable)
  _CCCL_API inline ~__dtor() = delete;

protected:
  _CCCL_API inline void __destroy() noexcept = delete;
};

#undef _LIBCUDACXX_VARIANT_DESTRUCTOR_BODY

template <class _Traits>
class _CCCL_TYPE_VISIBILITY_DEFAULT __ctor : public __dtor<_Traits>
{
  using __base_type = __dtor<_Traits>;

  template <size_t _CurrentIndex, class _Rhs>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr void
  __generic_construct_impl(integral_constant<size_t, _CurrentIndex>, const size_t __index, __ctor& __lhs, _Rhs&& __rhs)
  {
    if (__index == _CurrentIndex)
    {
      __construct_at(_CUDA_VSTD::addressof(__access::__base::__get_alt<_CurrentIndex>(__lhs.__as_base())),
                     in_place,
                     __access::__base::__get_alt<_CurrentIndex>(_CUDA_VSTD::forward<_Rhs>(__rhs).__as_base()).__value);
      return;
    }
    __generic_construct_impl(
      integral_constant<size_t, _CurrentIndex - 1>{}, __index, __lhs, _CUDA_VSTD::forward<_Rhs>(__rhs));
  }

  template <class _Rhs>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr void
  __generic_construct_impl(integral_constant<size_t, 0>, const size_t __index, __ctor& __lhs, _Rhs&& __rhs)
  {
    if (__index == 0)
    {
      __construct_at(_CUDA_VSTD::addressof(__access::__base::__get_alt<0>(__lhs.__as_base())),
                     in_place,
                     __access::__base::__get_alt<0>(_CUDA_VSTD::forward<_Rhs>(__rhs).__as_base()).__value);
      return;
    }
    // We already checked that every variant has a value, so we should never reach this line
    _CCCL_UNREACHABLE();
  }

public:
  using __base_type::__base_type;
  using __base_type::operator=;

protected:
  template <size_t _Ip, class _Tp, class... _Args>
  _CCCL_API inline static _Tp& __construct_alt(__alt<_Ip, _Tp>& __a, _Args&&... __args)
  {
    __construct_at(_CUDA_VSTD::addressof(__a), in_place, _CUDA_VSTD::forward<_Args>(__args)...);
    return __a.__value;
  }

  template <class _Rhs>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static void __generic_construct(__ctor& __lhs, _Rhs&& __rhs)
  {
    __lhs.__destroy();
    if (!__rhs.valueless_by_exception())
    {
      constexpr size_t __np = remove_cvref_t<__ctor>::__size();
      __generic_construct_impl(
        integral_constant<size_t, __np - 1>{}, __rhs.index(), __lhs, _CUDA_VSTD::forward<_Rhs>(__rhs));
      __lhs.__index = static_cast<decltype(__lhs.__index)>(__rhs.index());
    }
  }
};

template <class _Traits, _Trait = _Traits::__move_constructible_trait>
class _CCCL_TYPE_VISIBILITY_DEFAULT __move_constructor;

#define _LIBCUDACXX_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, move_constructor)                 \
  template <class... _Types>                                                                             \
  class _CCCL_TYPE_VISIBILITY_DEFAULT                                                                    \
  __move_constructor<__traits<_Types...>, move_constructible_trait> : public __ctor<__traits<_Types...>> \
  {                                                                                                      \
    using __base_type = __ctor<__traits<_Types...>>;                                                     \
                                                                                                         \
  public:                                                                                                \
    using __base_type::__base_type;                                                                      \
    using __base_type::operator=;                                                                        \
                                                                                                         \
    _CCCL_HIDE_FROM_ABI __move_constructor(const __move_constructor&)            = default;              \
    _CCCL_HIDE_FROM_ABI ~__move_constructor()                                    = default;              \
    _CCCL_HIDE_FROM_ABI __move_constructor& operator=(const __move_constructor&) = default;              \
    _CCCL_HIDE_FROM_ABI __move_constructor& operator=(__move_constructor&&)      = default;              \
    move_constructor                                                                                     \
  }

_LIBCUDACXX_VARIANT_MOVE_CONSTRUCTOR(_Trait::_TriviallyAvailable,
                                     _CCCL_HIDE_FROM_ABI __move_constructor(__move_constructor&& __that) = default;);

_LIBCUDACXX_VARIANT_MOVE_CONSTRUCTOR(
  _Trait::_Available,
  _CCCL_API inline __move_constructor(__move_constructor&& __that) noexcept(
    __all<is_nothrow_move_constructible_v<_Types>...>::value) : __move_constructor(__valueless_t{}) {
    this->__generic_construct(*this, _CUDA_VSTD::move(__that));
  });

_LIBCUDACXX_VARIANT_MOVE_CONSTRUCTOR(_Trait::_Unavailable, __move_constructor(__move_constructor&&) = delete;);

#undef _LIBCUDACXX_VARIANT_MOVE_CONSTRUCTOR

template <class _Traits, _Trait = _Traits::__copy_constructible_trait>
class _CCCL_TYPE_VISIBILITY_DEFAULT __copy_constructor;

#define _LIBCUDACXX_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, copy_constructor)                             \
  template <class... _Types>                                                                                         \
  class _CCCL_TYPE_VISIBILITY_DEFAULT                                                                                \
  __copy_constructor<__traits<_Types...>, copy_constructible_trait> : public __move_constructor<__traits<_Types...>> \
  {                                                                                                                  \
    using __base_type = __move_constructor<__traits<_Types...>>;                                                     \
                                                                                                                     \
  public:                                                                                                            \
    using __base_type::__base_type;                                                                                  \
    using __base_type::operator=;                                                                                    \
                                                                                                                     \
    _CCCL_HIDE_FROM_ABI __copy_constructor(__copy_constructor&&)                 = default;                          \
    _CCCL_HIDE_FROM_ABI ~__copy_constructor()                                    = default;                          \
    _CCCL_HIDE_FROM_ABI __copy_constructor& operator=(const __copy_constructor&) = default;                          \
    _CCCL_HIDE_FROM_ABI __copy_constructor& operator=(__copy_constructor&&)      = default;                          \
    copy_constructor                                                                                                 \
  }

_LIBCUDACXX_VARIANT_COPY_CONSTRUCTOR(
  _Trait::_TriviallyAvailable, _CCCL_HIDE_FROM_ABI __copy_constructor(const __copy_constructor& __that) = default;);

_LIBCUDACXX_VARIANT_COPY_CONSTRUCTOR(
  _Trait::_Available,
  _CCCL_API inline __copy_constructor(const __copy_constructor& __that) : __copy_constructor(__valueless_t{}) {
    this->__generic_construct(*this, __that);
  });

_LIBCUDACXX_VARIANT_COPY_CONSTRUCTOR(_Trait::_Unavailable, __copy_constructor(const __copy_constructor&) = delete;);

#undef _LIBCUDACXX_VARIANT_COPY_CONSTRUCTOR

template <class _Traits>
class _CCCL_TYPE_VISIBILITY_DEFAULT __assignment : public __copy_constructor<_Traits>
{
  using __base_type = __copy_constructor<_Traits>;

  template <size_t _CurrentIndex, class _Other>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN constexpr void
  __generic_assign(integral_constant<size_t, _CurrentIndex>, const size_t __index, _Other&& __rhs)
  {
    if (__index == _CurrentIndex)
    {
      this->__assign_alt(
        __access::__base::__get_alt<_CurrentIndex>(this->__as_base()),
        __access::__base::__get_alt<_CurrentIndex>(_CUDA_VSTD::forward<_Other>(__rhs).__as_base()).__value);
      return;
    }
    this->__generic_assign(integral_constant<size_t, _CurrentIndex - 1>{}, __index, _CUDA_VSTD::forward<_Other>(__rhs));
  }

  template <class _Other>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN constexpr void
  __generic_assign(integral_constant<size_t, 0>, const size_t __index, _Other&& __rhs)
  {
    if (__index == 0)
    {
      this->__assign_alt(__access::__base::__get_alt<0>(this->__as_base()),
                         __access::__base::__get_alt<0>(_CUDA_VSTD::forward<_Other>(__rhs).__as_base()).__value);
      return;
    }
    // We already checked that every variant has a value, so we should never reach this line
    _CCCL_UNREACHABLE();
  }

public:
  using __base_type::__base_type;
  using __base_type::operator=;

  template <size_t _Ip, class... _Args>
  _CCCL_API inline auto& __emplace(_Args&&... __args)
  {
    this->__destroy();
    auto& __res = this->__construct_alt(__access::__base::__get_alt<_Ip>(*this), _CUDA_VSTD::forward<_Args>(__args)...);
    this->__index = _Ip;
    return __res;
  }

protected:
  _CCCL_EXEC_CHECK_DISABLE
  template <size_t _Ip,
            class _Tp,
            class _Arg,
            enable_if_t<is_nothrow_constructible_v<_Tp, _Arg> || !is_nothrow_move_constructible_v<_Tp>, int> = 0>
  _CCCL_API inline void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg)
  {
    if (this->index() == _Ip)
    {
      __a.__value = _CUDA_VSTD::forward<_Arg>(__arg);
    }
    else
    {
      this->__emplace<_Ip>(_CUDA_VSTD::forward<_Arg>(__arg));
    }
  }

  _CCCL_EXEC_CHECK_DISABLE
  template <size_t _Ip,
            class _Tp,
            class _Arg,
            enable_if_t<!is_nothrow_constructible_v<_Tp, _Arg> && is_nothrow_move_constructible_v<_Tp>, int> = 0>
  _CCCL_API inline void __assign_alt(__alt<_Ip, _Tp>& __a, _Arg&& __arg)
  {
    if (this->index() == _Ip)
    {
      __a.__value = _CUDA_VSTD::forward<_Arg>(__arg);
    }
    else
    {
      this->__emplace<_Ip>(_Tp(_CUDA_VSTD::forward<_Arg>(__arg)));
    }
  }

  template <class _That>
  _CCCL_API inline void __generic_assign(_That&& __that)
  {
    if (this->valueless_by_exception() && __that.valueless_by_exception())
    {
      // do nothing.
    }
    else if (__that.valueless_by_exception())
    {
      this->__destroy();
    }
    else
    {
      constexpr size_t __np = remove_cvref_t<__assignment>::__size();
      this->__generic_assign(integral_constant<size_t, __np - 1>{}, __that.index(), _CUDA_VSTD::forward<_That>(__that));
    }
  }
};

template <class _Traits, _Trait = _Traits::__move_assignable_trait>
class _CCCL_TYPE_VISIBILITY_DEFAULT __move_assignment;

#define _LIBCUDACXX_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, move_assignment)                        \
  template <class... _Types>                                                                               \
  class _CCCL_TYPE_VISIBILITY_DEFAULT                                                                      \
  __move_assignment<__traits<_Types...>, move_assignable_trait> : public __assignment<__traits<_Types...>> \
  {                                                                                                        \
    using __base_type = __assignment<__traits<_Types...>>;                                                 \
                                                                                                           \
  public:                                                                                                  \
    using __base_type::__base_type;                                                                        \
    using __base_type::operator=;                                                                          \
                                                                                                           \
    _CCCL_HIDE_FROM_ABI __move_assignment(const __move_assignment&)            = default;                  \
    _CCCL_HIDE_FROM_ABI __move_assignment(__move_assignment&&)                 = default;                  \
    _CCCL_HIDE_FROM_ABI ~__move_assignment()                                   = default;                  \
    _CCCL_HIDE_FROM_ABI __move_assignment& operator=(const __move_assignment&) = default;                  \
    move_assignment                                                                                        \
  }

_LIBCUDACXX_VARIANT_MOVE_ASSIGNMENT(
  _Trait::_TriviallyAvailable, _CCCL_HIDE_FROM_ABI __move_assignment& operator=(__move_assignment&& __that) = default;);

_LIBCUDACXX_VARIANT_MOVE_ASSIGNMENT(
  _Trait::_Available,
  _CCCL_API inline __move_assignment&
  operator=(__move_assignment&& __that) noexcept(
    __all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_move_assignable_v<_Types>) ...>::value) {
    this->__generic_assign(_CUDA_VSTD::move(__that));
    return *this;
  });

_LIBCUDACXX_VARIANT_MOVE_ASSIGNMENT(_Trait::_Unavailable, __move_assignment& operator=(__move_assignment&&) = delete;);

#undef _LIBCUDACXX_VARIANT_MOVE_ASSIGNMENT

template <class _Traits, _Trait = _Traits::__copy_assignable_trait>
class _CCCL_TYPE_VISIBILITY_DEFAULT __copy_assignment;

#define _LIBCUDACXX_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, copy_assignment)                             \
  template <class... _Types>                                                                                    \
  class _CCCL_TYPE_VISIBILITY_DEFAULT                                                                           \
  __copy_assignment<__traits<_Types...>, copy_assignable_trait> : public __move_assignment<__traits<_Types...>> \
  {                                                                                                             \
    using __base_type = __move_assignment<__traits<_Types...>>;                                                 \
                                                                                                                \
  public:                                                                                                       \
    using __base_type::__base_type;                                                                             \
    using __base_type::operator=;                                                                               \
                                                                                                                \
    _CCCL_HIDE_FROM_ABI __copy_assignment(const __copy_assignment&)       = default;                            \
    _CCCL_HIDE_FROM_ABI __copy_assignment(__copy_assignment&&)            = default;                            \
    _CCCL_HIDE_FROM_ABI ~__copy_assignment()                              = default;                            \
    _CCCL_HIDE_FROM_ABI __copy_assignment& operator=(__copy_assignment&&) = default;                            \
    copy_assignment                                                                                             \
  }

_LIBCUDACXX_VARIANT_COPY_ASSIGNMENT(
  _Trait::_TriviallyAvailable,
  _CCCL_HIDE_FROM_ABI __copy_assignment& operator=(const __copy_assignment& __that) = default;);

_LIBCUDACXX_VARIANT_COPY_ASSIGNMENT(
  _Trait::_Available, _CCCL_API inline __copy_assignment& operator=(const __copy_assignment& __that) {
    this->__generic_assign(__that);
    return *this;
  });

_LIBCUDACXX_VARIANT_COPY_ASSIGNMENT(_Trait::_Unavailable,
                                    __copy_assignment& operator=(const __copy_assignment&) = delete;);

#undef _LIBCUDACXX_VARIANT_COPY_ASSIGNMENT

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT __impl : public __copy_assignment<__traits<_Types...>>
{
  using __base_type = __copy_assignment<__traits<_Types...>>;

  template <size_t _CurrentIndex>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr void
  __swap_value(integral_constant<size_t, _CurrentIndex>, const size_t __index, __impl& __lhs, __impl& __rhs)
  {
    if (__index == _CurrentIndex)
    {
      using _CUDA_VSTD::swap;
      swap(__access::__base::__get_alt<_CurrentIndex>(__lhs.__as_base()).__value,
           __access::__base::__get_alt<_CurrentIndex>(__rhs.__as_base()).__value);
      return;
    }
    __swap_value(integral_constant<size_t, _CurrentIndex - 1>{}, __index, __lhs, __rhs);
  }

  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr void
  __swap_value(integral_constant<size_t, 0>, const size_t __index, __impl& __lhs, __impl& __rhs)
  {
    if (__index == 0)
    {
      using _CUDA_VSTD::swap;
      swap(__access::__base::__get_alt<0>(__lhs.__as_base()).__value,
           __access::__base::__get_alt<0>(__rhs.__as_base()).__value);
      return;
    }
    // We already checked that every variant has a value, so we should never reach this line
    _CCCL_UNREACHABLE();
  }

public:
  using __base_type::__base_type; // get in_place_index_t constructor & friends
  _CCCL_HIDE_FROM_ABI __impl(__impl const&)            = default;
  _CCCL_HIDE_FROM_ABI __impl(__impl&&)                 = default;
  _CCCL_HIDE_FROM_ABI __impl& operator=(__impl const&) = default;
  _CCCL_HIDE_FROM_ABI __impl& operator=(__impl&&)      = default;

  template <size_t _Ip, class _Arg>
  _CCCL_API inline void __assign(_Arg&& __arg)
  {
    this->__assign_alt(__access::__base::__get_alt<_Ip>(*this), _CUDA_VSTD::forward<_Arg>(__arg));
  }

  _CCCL_API inline void __swap(__impl& __that)
  {
    if (this->valueless_by_exception() && __that.valueless_by_exception())
    {
      // do nothing.
    }
    else if (this->index() == __that.index())
    {
      constexpr size_t __np = remove_cvref_t<__impl>::__size();
      __swap_value(integral_constant<size_t, __np - 1>{}, this->index(), *this, __that);
    }
    else
    {
      __impl* __lhs = this;
      __impl* __rhs = _CUDA_VSTD::addressof(__that);
      if (__lhs->__move_nothrow() && !__rhs->__move_nothrow())
      {
        _CUDA_VSTD::swap(__lhs, __rhs);
      }
      __impl __tmp(_CUDA_VSTD::move(*__rhs));
      this->__generic_construct(*__rhs, _CUDA_VSTD::move(*__lhs));
      this->__generic_construct(*__lhs, _CUDA_VSTD::move(__tmp));
    }
  }

private:
  _CCCL_API constexpr bool __move_nothrow() const
  {
    constexpr bool __results[] = {is_nothrow_move_constructible_v<_Types>...};
    return this->valueless_by_exception() || __results[this->index()];
  }
};

struct __no_narrowing_check
{
  template <class _Dest, class _Source>
  using _Apply = type_identity<_Dest>;
};

struct __narrowing_check
{
  template <class _Dest, class _Source, bool = __cccl_internal::__is_non_narrowing_convertible<_Dest, _Source>::value>
  struct __narrowing_check_impl
  {};

  template <class _Dest, class _Source>
  struct __narrowing_check_impl<_Dest, _Source, true>
  {
    using type = type_identity<_Dest>;
  };

  template <class _Dest, class _Source>
  using _Apply _CCCL_NODEBUG_ALIAS = typename __narrowing_check_impl<_Dest, _Source>::type;
};

template <class _Dest, class _Source>
using __check_for_narrowing _CCCL_NODEBUG_ALIAS = typename _If<
#ifdef _LIBCUDACXX_ENABLE_NARROWING_CONVERSIONS_IN_VARIANT
  false &&
#endif // _LIBCUDACXX_ENABLE_NARROWING_CONVERSIONS_IN_VARIANT
    is_arithmetic_v<_Dest>,
  __narrowing_check,
  __no_narrowing_check>::template _Apply<_Dest, _Source>;

template <class _Tp, size_t _Idx>
struct __overload
{
  template <class _Up>
  _CCCL_API inline auto operator()(_Tp, _Up&&) const -> __check_for_narrowing<_Tp, _Up>;
};

template <class _Tp, size_t>
struct __overload_bool
{
  template <class _Up, class _Ap = remove_cvref_t<_Up>>
  _CCCL_API inline auto operator()(bool, _Up&&) const -> enable_if_t<is_same_v<_Ap, bool>, type_identity<_Tp>>;
};

template <size_t _Idx>
struct __overload<bool, _Idx> : __overload_bool<bool, _Idx>
{};
template <size_t _Idx>
struct __overload<bool const, _Idx> : __overload_bool<bool const, _Idx>
{};
template <size_t _Idx>
struct __overload<bool volatile, _Idx> : __overload_bool<bool volatile, _Idx>
{};
template <size_t _Idx>
struct __overload<bool const volatile, _Idx> : __overload_bool<bool const volatile, _Idx>
{};

template <class... _Bases>
struct __all_overloads : _Bases...
{
  _CCCL_API inline void operator()() const;
  using _Bases::operator()...;
};

template <class IdxSeq>
struct __make_overloads_imp;

template <size_t... _Idx>
struct __make_overloads_imp<__tuple_indices<_Idx...>>
{
  template <class... _Types>
  using _Apply _CCCL_NODEBUG_ALIAS = __all_overloads<__overload<_Types, _Idx>...>;
};

template <class... _Types>
using _MakeOverloads _CCCL_NODEBUG_ALIAS =
  typename __make_overloads_imp<__make_indices_imp<sizeof...(_Types), 0>>::template _Apply<_Types...>;

template <class _Tp, class... _Types>
using __best_match_t = typename invoke_result_t<_MakeOverloads<_Types...>, _Tp, _Tp>::type;

struct __invalid_variant_constraints
{
  static constexpr bool __constructible         = false;
  static constexpr bool __nothrow_constructible = false;
  static constexpr bool __assignable            = false;
  static constexpr bool __nothrow_assignable    = false;
};

template <class... _Types>
struct __variant_constraints
{
  template <class _Arg, class _Tp = __best_match_t<_Arg, _Types...>>
  struct __match_construct
  {
    static constexpr size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value;

    static constexpr bool __constructible         = is_constructible_v<_Tp, _Arg>;
    static constexpr bool __nothrow_constructible = is_nothrow_constructible_v<_Tp, _Arg>;
  };

  template <size_t _Ip, class... _Args>
  struct __variadic_construct
  {
    using _Tp = variant_alternative_t<_Ip, variant<_Types...>>;

    static constexpr bool __constructible         = is_constructible_v<_Tp, _Args...>;
    static constexpr bool __nothrow_constructible = is_nothrow_constructible_v<_Tp, _Args...>;
  };

  template <size_t _Ip, class _Up, class... _Args>
  struct __variadic_ilist_construct
  {
    using _Tp = variant_alternative_t<_Ip, variant<_Types...>>;

    static constexpr bool __constructible         = is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
    static constexpr bool __nothrow_constructible = is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>;
  };

  template <class _Arg, class _Tp = __best_match_t<_Arg, _Types...>>
  struct __match_assign
  {
    static constexpr size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value;

    static constexpr bool __assignable = is_assignable_v<_Tp&, _Arg> && is_constructible_v<_Tp, _Arg>;
    static constexpr bool __nothrow_assignable =
      is_nothrow_assignable_v<_Tp&, _Arg> && is_nothrow_constructible_v<_Tp, _Arg>;
  };

  template <bool>
  struct __swappable
  {
    static constexpr bool __is_swappable =
      __all<(is_move_constructible_v<_Types> && is_swappable_v<_Types>) ...>::value;

    static constexpr bool __is_nothrow_swappable =
      __all<(is_nothrow_move_constructible_v<_Types> && is_nothrow_swappable_v<_Types>) ...>::value;
  };
};
} // namespace __variant_detail

template <class... _Types>
class _CCCL_TYPE_VISIBILITY_DEFAULT variant
    : private __sfinae_base<__all<is_copy_constructible_v<_Types>...>::value,
                            __all<is_move_constructible_v<_Types>...>::value,
                            __all<(is_copy_constructible_v<_Types> && is_copy_assignable_v<_Types>) ...>::value,
                            __all<(is_move_constructible_v<_Types> && is_move_assignable_v<_Types>) ...>::value>
{
  static_assert(0 < sizeof...(_Types), "variant must consist of at least one alternative.");

  static_assert(__all<!is_array_v<_Types>...>::value, "variant can not have an array type as an alternative.");

  static_assert(__all<!is_reference_v<_Types>...>::value, "variant can not have a reference type as an alternative.");

  static_assert(__all<!is_void_v<_Types>...>::value, "variant can not have a void type as an alternative.");

  using __first_type  = variant_alternative_t<0, variant>;
  using __constraints = __variant_detail::__variant_constraints<_Types...>;

public:
  // Needs to be dependent to guard against incomplete types
  template <bool _Dummy = true,
            class       = enable_if_t<__dependent_type<is_default_constructible<__first_type>, _Dummy>::value>>
  _CCCL_API constexpr variant() noexcept(is_nothrow_default_constructible_v<__first_type>)
      : __impl_(in_place_index<0>)
  {}

  _CCCL_HIDE_FROM_ABI constexpr variant(const variant&) = default;
  _CCCL_HIDE_FROM_ABI constexpr variant(variant&&)      = default;

  template <class _Arg>
  using __match_construct =
    _If<!is_same_v<remove_cvref_t<_Arg>, variant> && !__is_inplace_type<remove_cvref_t<_Arg>>::value //
          && !__is_inplace_index<remove_cvref_t<_Arg>>::value,
        typename __constraints::template __match_construct<_Arg>,
        __variant_detail::__invalid_variant_constraints>;

  // CTAD fails if we do not SFINAE the empty variant away first
  template <class _Arg,
            class              = enable_if_t<sizeof...(_Types) != 0>,
            class _Constraints = __match_construct<_Arg>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API constexpr variant(_Arg&& __arg) noexcept(_Constraints::__nothrow_constructible)
      : __impl_(in_place_index<_Constraints::_Ip>, _CUDA_VSTD::forward<_Arg>(__arg))
  {}

  template <size_t _Ip, class... _Args>
  using __variadic_construct =
    _If<(_Ip < sizeof...(_Types)),
        typename __constraints::template __variadic_construct<_Ip, _Args...>,
        __variant_detail::__invalid_variant_constraints>;

  template <size_t _Ip,
            class... _Args,
            class _Constraints = __variadic_construct<_Ip, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API explicit constexpr variant(in_place_index_t<_Ip>,
                                       _Args&&... __args) noexcept(_Constraints::__nothrow_constructible)
      : __impl_(in_place_index<_Ip>, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  template <size_t _Ip, class _Up, class... _Args>
  using __variadic_ilist_construct =
    _If<(_Ip < sizeof...(_Types)),
        typename __constraints::template __variadic_ilist_construct<_Ip, _Up, _Args...>,
        __variant_detail::__invalid_variant_constraints>;

  template <size_t _Ip,
            class _Up,
            class... _Args,
            class _Constraints = __variadic_ilist_construct<_Ip, _Up, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API explicit constexpr variant(in_place_index_t<_Ip>, initializer_list<_Up> __il, _Args&&... __args) noexcept(
    _Constraints::__nothrow_constructible)
      : __impl_(in_place_index<_Ip>, __il, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  template <class _Tp,
            class... _Args,
            size_t _Ip         = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
            class _Constraints = __variadic_construct<_Ip, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API explicit constexpr variant(in_place_type_t<_Tp>,
                                       _Args&&... __args) noexcept(_Constraints::__nothrow_constructible)
      : __impl_(in_place_index<_Ip>, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  template <class _Tp,
            class _Up,
            class... _Args,
            size_t _Ip         = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
            class _Constraints = __variadic_ilist_construct<_Ip, _Up, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API explicit constexpr variant(in_place_type_t<_Tp>, initializer_list<_Up> __il, _Args&&... __args) noexcept(
    _Constraints::__nothrow_constructible)
      : __impl_(in_place_index<_Ip>, __il, _CUDA_VSTD::forward<_Args>(__args)...)
  {}

  _CCCL_HIDE_FROM_ABI ~variant() = default;

  _CCCL_HIDE_FROM_ABI constexpr variant& operator=(const variant&) = default;
  _CCCL_HIDE_FROM_ABI constexpr variant& operator=(variant&&)      = default;

  template <class _Arg>
  using __match_assign =
    _If<!is_same_v<remove_cvref_t<_Arg>, variant>,
        typename __constraints::template __match_assign<_Arg>,
        __variant_detail::__invalid_variant_constraints>;

  template <class _Arg, class _Constraints = __match_assign<_Arg>, class = enable_if_t<_Constraints::__assignable>>
  _CCCL_API inline variant& operator=(_Arg&& __arg) noexcept(_Constraints::__nothrow_assignable)
  {
    __impl_.template __assign<_Constraints::_Ip>(_CUDA_VSTD::forward<_Arg>(__arg));
    return *this;
  }

  template <size_t _Ip,
            class... _Args,
            class _Constraints = __variadic_construct<_Ip, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API inline typename _Constraints::_Tp& emplace(_Args&&... __args)
  {
    return __impl_.template __emplace<_Ip>(_CUDA_VSTD::forward<_Args>(__args)...);
  }

  template <size_t _Ip,
            class _Up,
            class... _Args,
            class _Constraints = __variadic_ilist_construct<_Ip, _Up, _Args...>,
            class              = enable_if_t<_Constraints::__constructible>>
  _CCCL_API inline typename _Constraints::_Tp& emplace(initializer_list<_Up> __il, _Args&&... __args)
  {
    return __impl_.template __emplace<_Ip>(__il, _CUDA_VSTD::forward<_Args>(__args)...);
  }

  template <class _Tp,
            class... _Args,
            size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
            enable_if_t<is_constructible_v<_Tp, _Args...>, int> = 0>
  _CCCL_API inline _Tp& emplace(_Args&&... __args)
  {
    return __impl_.template __emplace<_Ip>(_CUDA_VSTD::forward<_Args>(__args)...);
  }

  template <class _Tp,
            class _Up,
            class... _Args,
            size_t _Ip = __find_detail::__find_unambiguous_index_sfinae<_Tp, _Types...>::value,
            enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>, int> = 0>
  _CCCL_API inline _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args)
  {
    return __impl_.template __emplace<_Ip>(__il, _CUDA_VSTD::forward<_Args>(__args)...);
  }

  _CCCL_API constexpr bool valueless_by_exception() const noexcept
  {
    return __impl_.valueless_by_exception();
  }

  _CCCL_API constexpr size_t index() const noexcept
  {
    return __impl_.index();
  }

  // Needs to be dependent to guard against incomplete types
  template <bool _Dummy>
  using __swap_constraint =
    __dependent_type<typename __variant_detail::__variant_constraints<_Types...>::template __swappable<_Dummy>, _Dummy>;

  template <bool _Dummy       = true,
            class _Constraint = __swap_constraint<_Dummy>,
            class             = enable_if_t<_Constraint::__is_swappable>>
  _CCCL_API inline void swap(variant& __that) noexcept(_Constraint::__is_nothrow_swappable)
  {
    __impl_.__swap(__that.__impl_);
  }

  _CCCL_API static constexpr size_t __size() noexcept
  {
    return sizeof...(_Types);
  }

private:
  __variant_detail::__impl<_Types...> __impl_;

  friend struct __variant_detail::__access::__variant;
  friend struct __variant_detail::__visitation::__variant;
};

template <size_t _Ip, class... _Types>
_CCCL_API constexpr bool __holds_alternative(const variant<_Types...>& __v) noexcept
{
  return __v.index() == _Ip;
}

template <class _Tp, class... _Types>
_CCCL_API constexpr bool holds_alternative(const variant<_Types...>& __v) noexcept
{
  return _CUDA_VSTD::__holds_alternative<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <size_t _Ip, class _Vp>
_CCCL_API constexpr auto&& __generic_get(_Vp&& __v)
{
  using __variant_detail::__access::__variant;
  if (!_CUDA_VSTD::__holds_alternative<_Ip>(__v))
  {
    _CUDA_VSTD::__throw_bad_variant_access();
  }
  return __variant::__get_alt<_Ip>(_CUDA_VSTD::forward<_Vp>(__v)).__value;
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr variant_alternative_t<_Ip, variant<_Types...>>& get(variant<_Types...>& __v)
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get<_Ip>(__v);
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr variant_alternative_t<_Ip, variant<_Types...>>&& get(variant<_Types...>&& __v)
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get<_Ip>(_CUDA_VSTD::move(__v));
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr const variant_alternative_t<_Ip, variant<_Types...>>& get(const variant<_Types...>& __v)
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get<_Ip>(__v);
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr const variant_alternative_t<_Ip, variant<_Types...>>&& get(const variant<_Types...>&& __v)
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get<_Ip>(_CUDA_VSTD::move(__v));
}

template <class _Tp, class... _Types>
_CCCL_API constexpr _Tp& get(variant<_Types...>& __v)
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <class _Tp, class... _Types>
_CCCL_API constexpr _Tp&& get(variant<_Types...>&& __v)
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get<__find_exactly_one_t<_Tp, _Types...>::value>(_CUDA_VSTD::move(__v));
}

template <class _Tp, class... _Types>
_CCCL_API constexpr const _Tp& get(const variant<_Types...>& __v)
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <class _Tp, class... _Types>
_CCCL_API constexpr const _Tp&& get(const variant<_Types...>&& __v)
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get<__find_exactly_one_t<_Tp, _Types...>::value>(_CUDA_VSTD::move(__v));
}

template <size_t _Ip, class _Vp>
_CCCL_API constexpr auto* __generic_get_if(_Vp* __v) noexcept
{
  using __variant_detail::__access::__variant;
  return __v && _CUDA_VSTD::__holds_alternative<_Ip>(*__v)
         ? _CUDA_VSTD::addressof(__variant::__get_alt<_Ip>(*__v).__value)
         : nullptr;
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr add_pointer_t<variant_alternative_t<_Ip, variant<_Types...>>>
get_if(variant<_Types...>* __v) noexcept
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get_if<_Ip>(__v);
}

template <size_t _Ip, class... _Types>
_CCCL_API constexpr add_pointer_t<const variant_alternative_t<_Ip, variant<_Types...>>>
get_if(const variant<_Types...>* __v) noexcept
{
  static_assert(_Ip < sizeof...(_Types), "");
  static_assert(!is_void_v<variant_alternative_t<_Ip, variant<_Types...>>>, "");
  return _CUDA_VSTD::__generic_get_if<_Ip>(__v);
}

template <class _Tp, class... _Types>
_CCCL_API constexpr add_pointer_t<_Tp> get_if(variant<_Types...>* __v) noexcept
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get_if<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <class _Tp, class... _Types>
_CCCL_API constexpr add_pointer_t<const _Tp> get_if(const variant<_Types...>* __v) noexcept
{
  static_assert(!is_void_v<_Tp>, "");
  return _CUDA_VSTD::get_if<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <class _Operator>
struct __convert_to_bool
{
  template <class _T1, class _T2>
  _CCCL_API constexpr bool operator()(_T1&& __t1, _T2&& __t2) const
  {
    static_assert(
      is_convertible_v<decltype(_Operator{}(_CUDA_VSTD::forward<_T1>(__t1), _CUDA_VSTD::forward<_T2>(__t2))), bool>,
      "the relational operator does not return a type which is "
      "implicitly convertible to bool");
    return _Operator{}(_CUDA_VSTD::forward<_T1>(__t1), _CUDA_VSTD::forward<_T2>(__t2));
  }
};

struct __variant_binary_visitor
{
  template <class _BinaryOp, class _LeftVariant, class _RightVariant>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr auto
  __visit(const size_t __index, _BinaryOp&& __op, const _LeftVariant& __lhs, const _RightVariant& __rhs)
  {
    return __visit(integral_constant<size_t, variant_size_v<_LeftVariant> - 1>{},
                   __index,
                   _CUDA_VSTD::forward<_BinaryOp>(__op),
                   __lhs,
                   __rhs);
  }

private:
  template <size_t _CurrentIndex, class _BinaryOp, class _LeftVariant, class _RightVariant>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr auto
  __visit(integral_constant<size_t, _CurrentIndex>,
          const size_t __index,
          _BinaryOp&& __op,
          const _LeftVariant& __lhs,
          const _RightVariant& __rhs)
  {
    if (__index == _CurrentIndex)
    {
      return __op(_CUDA_VSTD::get<_CurrentIndex>(__lhs), _CUDA_VSTD::get<_CurrentIndex>(__rhs));
    }
    return __visit(
      integral_constant<size_t, _CurrentIndex - 1>{}, __index, _CUDA_VSTD::forward<_BinaryOp>(__op), __lhs, __rhs);
  }

  template <class _BinaryOp, class _LeftVariant, class _RightVariant>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static constexpr auto
  __visit(integral_constant<size_t, 0>,
          const size_t __index,
          _BinaryOp&& __op,
          const _LeftVariant& __lhs,
          const _RightVariant& __rhs)
  {
    if (__index == 0)
    {
      return __op(_CUDA_VSTD::get<0>(__lhs), _CUDA_VSTD::get<0>(__rhs));
    }
    // We already checked that every variant has a value, so we should never reach this line
#if _CCCL_COMPILER(MSVC) // MSVC needs this to be wrapped in a function or it will error
    _CUDA_VSTD::unreachable();
#else // ^^^ _CCCL_COMPILER(MSVC) ^^^ / vvv !_CCCL_COMPILER(MSVC) vvv
    _CCCL_UNREACHABLE();
#endif // !_CCCL_COMPILER(MSVC)
  }
};

template <class... _Types>
_CCCL_API constexpr bool operator==(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  const auto __index = __lhs.index();
  if (__index != __rhs.index())
  {
    return false;
  }
  if (__lhs.valueless_by_exception())
  {
    return true;
  }

  return __variant_binary_visitor::__visit(__index, __convert_to_bool<equal_to<>>{}, __lhs, __rhs);
}

#if _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()

template <class... _Types>
  requires(three_way_comparable<_Types> && ...)
_CCCL_API constexpr common_comparison_category_t<compare_three_way_result_t<_Types>...>
operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  using __result_t = common_comparison_category_t<compare_three_way_result_t<_Types>...>;
  if (__lhs.valueless_by_exception() && __rhs.valueless_by_exception())
  {
    return strong_ordering::equal;
  }
  if (__lhs.valueless_by_exception())
  {
    return strong_ordering::less;
  }
  if (__rhs.valueless_by_exception())
  {
    return strong_ordering::greater;
  }
  if (auto __c = __lhs.index() <=> __rhs.index(); __c != 0)
  {
    return __c;
  }
  auto __three_way = []<class _Type>(const _Type& __v, const _Type& __w) -> __result_t {
    return __v <=> __w;
  };
  return __variant_binary_visitor::__visit(__lhs.index(), __three_way, __lhs, __rhs);
}

#endif // _LIBCUDACXX_HAS_SPACESHIP_OPERATOR()

template <class... _Types>
_CCCL_API constexpr bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  if (__lhs.index() != __rhs.index())
  {
    return true;
  }
  if (__lhs.valueless_by_exception())
  {
    return false;
  }
  return __variant_binary_visitor::__visit(__lhs.index(), __convert_to_bool<not_equal_to<>>{}, __lhs, __rhs);
}

template <class... _Types>
_CCCL_API constexpr bool operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  if (__rhs.valueless_by_exception())
  {
    return false;
  }
  if (__lhs.valueless_by_exception())
  {
    return true;
  }
  if (__lhs.index() < __rhs.index())
  {
    return true;
  }
  if (__lhs.index() > __rhs.index())
  {
    return false;
  }
  return __variant_binary_visitor::__visit(__lhs.index(), __convert_to_bool<less<>>{}, __lhs, __rhs);
}

template <class... _Types>
_CCCL_API constexpr bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  if (__lhs.valueless_by_exception())
  {
    return false;
  }
  if (__rhs.valueless_by_exception())
  {
    return true;
  }
  if (__lhs.index() > __rhs.index())
  {
    return true;
  }
  if (__lhs.index() < __rhs.index())
  {
    return false;
  }
  return __variant_binary_visitor::__visit(__lhs.index(), __convert_to_bool<greater<>>{}, __lhs, __rhs);
}

template <class... _Types>
_CCCL_API constexpr bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  if (__lhs.valueless_by_exception())
  {
    return true;
  }
  if (__rhs.valueless_by_exception())
  {
    return false;
  }
  if (__lhs.index() < __rhs.index())
  {
    return true;
  }
  if (__lhs.index() > __rhs.index())
  {
    return false;
  }
  return __variant_binary_visitor::__visit(__lhs.index(), __convert_to_bool<less_equal<>>{}, __lhs, __rhs);
}

template <class... _Types>
_CCCL_API constexpr bool operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs)
{
  if (__rhs.valueless_by_exception())
  {
    return true;
  }
  if (__lhs.valueless_by_exception())
  {
    return false;
  }
  if (__lhs.index() > __rhs.index())
  {
    return true;
  }
  if (__lhs.index() < __rhs.index())
  {
    return false;
  }
  return __variant_binary_visitor::__visit(__lhs.index(), __convert_to_bool<greater_equal<>>{}, __lhs, __rhs);
}

template <class... _Vs>
_CCCL_API constexpr void __throw_if_valueless(_Vs&&... __vs)
{
  [[maybe_unused]] int __unused[] = {
    (_CUDA_VSTD::__as_variant(__vs).valueless_by_exception() ? _CUDA_VSTD::__throw_bad_variant_access() : void(), 0)...,
    0};
}

template <class _Visitor,
          class... _Vs,
          typename = void_t<decltype(_CUDA_VSTD::__as_variant(_CUDA_VSTD::declval<_Vs>()))...>>
_CCCL_API constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs)
{
  using __variant_detail::__visitation::__variant;
  _CUDA_VSTD::__throw_if_valueless(_CUDA_VSTD::forward<_Vs>(__vs)...);
  return __variant::__visit_value(_CUDA_VSTD::forward<_Visitor>(__visitor), _CUDA_VSTD::forward<_Vs>(__vs)...);
}

template <class _Rp,
          class _Visitor,
          class... _Vs,
          typename = void_t<decltype(_CUDA_VSTD::__as_variant(_CUDA_VSTD::declval<_Vs>()))...>>
_CCCL_API constexpr _Rp visit(_Visitor&& __visitor, _Vs&&... __vs)
{
  using __variant_detail::__visitation::__variant;
  _CUDA_VSTD::__throw_if_valueless(_CUDA_VSTD::forward<_Vs>(__vs)...);
  return __variant::__visit_value<_Rp>(_CUDA_VSTD::forward<_Visitor>(__visitor), _CUDA_VSTD::forward<_Vs>(__vs)...);
}

template <class... _Types>
_CCCL_API inline auto swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs) noexcept(noexcept(__lhs.swap(__rhs)))
  -> decltype(__lhs.swap(__rhs))
{
  return __lhs.swap(__rhs);
}

#ifndef __cuda_std__
template <class... _Types>
struct _CCCL_TYPE_VISIBILITY_DEFAULT hash<__enable_hash_helper<variant<_Types...>, remove_const_t<_Types>...>>
{
  using argument_type = variant<_Types...>;
  using result_type   = size_t;

  template <size_t _CurrentIndex>
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static size_t
  __hash(integral_constant<size_t, _CurrentIndex>, const size_t __index, const argument_type& __v) noexcept
  {
    if (__index == _CurrentIndex)
    {
      using __value_type = remove_const_t<__type_index_c<_CurrentIndex, _Types...>>;
      return hash<__value_type>{}(__access::__base::__get_alt<_CurrentIndex>(this->__as_base()).__value);
    }
    __hash(integral_constant<size_t, _CurrentIndex - 1>{}, __index, __v);
  }
  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN static size_t
  __hash(integral_constant<size_t, 0>, const size_t __index, const argument_type& __v) noexcept
  {
    if (__index == 0)
    {
      using __value_type = remove_const_t<__type_index_c<0, _Types...>>;
      return hash<__value_type>{}(__access::__base::__get_alt<0>(this->__as_base()).__value);
    }
    // We already checked that every variant has a value, so we should never reach this line
    _CCCL_UNREACHABLE();
  }

  _CCCL_API inline _CCCL_VISIBILITY_HIDDEN result_type operator()(const argument_type& __v) const
  {
    size_t __res = __v.valueless_by_exception()
                   ? 299792458 // Random value chosen by the universe upon creation
                   : __hash(integral_constant<size_t, sizeof...(_Types) - 1>{}, __v.index(), __v);
    return _CUDA_VSTD::__hash_combine(__res, hash<size_t>{}(__v.index()));
  }
};
#endif // __cuda_std__

// __unchecked_get is the same as _CUDA_VSTD::get, except, it is UB to use it
// with the wrong type whereas _CUDA_VSTD::get will throw or returning nullptr.
// This makes it faster than _CUDA_VSTD::get.
template <size_t _Ip, class _Vp>
_CCCL_API constexpr auto&& __unchecked_get(_Vp&& __v) noexcept
{
  using __variant_detail::__access::__variant;
  return __variant::__get_alt<_Ip>(_CUDA_VSTD::forward<_Vp>(__v)).__value;
}

template <class _Tp, class... _Types>
_CCCL_API constexpr auto&& __unchecked_get(const variant<_Types...>& __v) noexcept
{
  return _CUDA_VSTD::__unchecked_get<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

template <class _Tp, class... _Types>
_CCCL_API constexpr auto&& __unchecked_get(variant<_Types...>& __v) noexcept
{
  return _CUDA_VSTD::__unchecked_get<__find_exactly_one_t<_Tp, _Types...>::value>(__v);
}

_CCCL_END_NAMESPACE_CUDA_STD

#include <cuda/std/__cccl/epilogue.h>

#endif // _LIBCUDACXX_VARIANT
